﻿using Blazor.DownloadFileFast.Interfaces;
using System.Net;
using System.Net.Http.Json;
using System.Web;

namespace Xms.Abp;

public abstract class CrudService<TPagingListViewModel, TKey, TPagedParameter, TDetailsViewModel, TCreateViewModel, TUpdateViewModel> : ICrudDetailsService<TDetailsViewModel, TKey>, ICrudCreateService<TCreateViewModel>, ICrudUpdateService<TUpdateViewModel, TKey>, ICrudReviewService<TUpdateViewModel, TKey>, ICrudService<TKey>
    where TDetailsViewModel : class, new()
    where TKey : struct
{
    protected HttpApiClient HttpClient { get; set; }
    protected IBlazorDownloadFileService DownloadFileService { get; set; }

    public CrudService(HttpApiClient httpClient, IBlazorDownloadFileService downloadFileService)
    {
        HttpClient = httpClient;
        DownloadFileService = downloadFileService;
    }

    protected abstract string PrefixPath { get; }
    protected virtual string GetListUrl => PrefixPath + "/page";
    protected virtual string GetDetailsUrl => PrefixPath + "/{id}";
    protected virtual string CreateUrl => PrefixPath + "/create";
    protected virtual string UpdateUrl => PrefixPath + "/{id}/update";
    protected virtual string DeleteUrl => PrefixPath + "/{id}/delete";
    protected virtual string DeleteManyUrl => PrefixPath + "/delete";
    protected virtual string ExportUrl => PrefixPath + "/export";
    public virtual string ImportUrl => PrefixPath + "/import";
    protected virtual string DownloadTemplateUrl => PrefixPath + "/templet";
    protected virtual string ToggleEnabledUrl => PrefixPath + "/{id}/enabled/toggle?isEnabled={isEnabled}";
    protected virtual string NameValuesUrl => PrefixPath + "/name-values";
    protected virtual string NameNosUrl => PrefixPath + "/name-nos";
    protected virtual string SubmitReviewUrl => PrefixPath + "/{id}/submit/review";
    protected virtual string ReviewUrl => PrefixPath + "/{id}/review";
    public virtual async Task<PagedResult<TPagingListViewModel>> GetListAsync(TPagedParameter parameter)
    {
        return await HttpClient.PostAsync<PagedResult<TPagingListViewModel>>(GetListUrl, parameter);
    }
    public virtual async Task<TDetailsViewModel> GetDetailsAsync(TKey id)
    {
        return await HttpClient.GetAsync<TDetailsViewModel>(GetDetailsUrl.Replace("{id}", id.ToString()));
    }
    public virtual async Task<bool> CreateAsync(TCreateViewModel model)
    {
        var result = true;
        await HttpClient.PostAsync(CreateUrl, model, async msg =>
        {
            result = false;
            await Task.CompletedTask;
        });
        return result;
    }
    public virtual async Task<bool> UpdateAsync(TKey id, TUpdateViewModel model)
    {
        var result = true;
        await HttpClient.PutAsync(UpdateUrl.Replace("{id}", id.ToString()), model, async msg =>
        {
            result = false;
            await Task.CompletedTask;
        });
        return result;
    }
    public virtual async Task<bool> DeleteAsync(TKey id)
    {
        var result = true;
        await HttpClient.DeleteAsync(DeleteUrl.Replace("{id}", id.ToString()), async msg =>
        {
            result = false;
            await Task.CompletedTask;
        });
        return result;
    }
    public virtual async Task<bool> DeleteManyAsync(TKey[] ids)
    {
        var result = true;
        var query = ids.Select(r => $"ids[]={r}").JoinAsString("&");
        await HttpClient.DeleteAsync(DeleteManyUrl + "?" + query, async msg =>
        {
            result = false;
            await Task.CompletedTask;
        });
        return result;
    }
    public virtual async Task<bool> ExportAsync(TPagedParameter parameter)
    {
        var result = false;
        var response = await HttpClient.HttpClient.PostAsJsonAsync(ExportUrl, parameter);
        if (response != null && response.IsSuccessStatusCode)
        {
            await DownloadFileService.DownloadFileAsync(response.Content.Headers.ContentDisposition.FileNameStar, await response.Content.ReadAsByteArrayAsync(), response.Content.Headers.ContentType.ToString());
            result = true;
        }
        else
        {
            await HttpClient.MessageService.Error("数据导出失败", 2);
        }
        return result;
    }
    public virtual async Task<bool> DownloadTemplateAsync()
    {
        var result = false;
        var response = await HttpClient.HttpClient.PostAsync(DownloadTemplateUrl, new StringContent(""));
        if (response != null && response.IsSuccessStatusCode)
        {
            //var fileName = HttpUtility.UrlDecode(response.Content.Headers.ContentDisposition.FileNameStar, Encoding.UTF8);
            await DownloadFileService.DownloadFileAsync(response.Content.Headers.ContentDisposition.FileNameStar, await response.Content.ReadAsByteArrayAsync(), response.Content.Headers.ContentType.ToString());
            result = true;
        }
        else
        {
            await HttpClient.MessageService.Error("下载导出模板失败", 2);
        }
        return result;
    }
    public virtual async Task<bool> ToggleEnabledAsync(TKey id, bool isEnabled)
    {
        var result = true;
        await HttpClient.PutAsync(ToggleEnabledUrl.Replace("{id}", id.ToString()).Replace("{isEnabled}", isEnabled.ToString().ToLower()), new { }, async msg =>
        {
            result = false;
            await Task.CompletedTask;
        });
        return result;
    }
    public virtual async Task<List<NameValue<TKey>>> GetNameValuesAsync(string filter = null)
    {
        var data = await HttpClient.GetAsync<List<NameValue<TKey>>>(NameValuesUrl + (filter.IsNullOrWhiteSpace() ? "" : $"?filter={filter}"));
        return data;
    }
    public virtual async Task<List<NameValue<TKey?>>> GetNNameValuesAsync(string filter = null)
    {
        var data = await HttpClient.GetAsync<List<NameValue<TKey?>>>(NameValuesUrl + (filter.IsNullOrWhiteSpace() ? "" : $"?filter={filter}"));
        return data;
    }

    public virtual async Task<List<NameValue>> GetNameNosAsync(string filter = null)
    {
        var data = await HttpClient.GetAsync<List<NameValue>>(NameNosUrl + (filter.IsNullOrWhiteSpace() ? "" : $"?filter={filter}"));
        return data;
    }

    public virtual async Task<bool> SubmitReviewAsync(TKey id)
    {
        var result = true;
        await HttpClient.PutAsync(SubmitReviewUrl.Replace("{id}", id.ToString()), new { }, async msg =>
        {
            result = false;
            await Task.CompletedTask;
        });
        return result;
    }
    public virtual async Task<bool> ReviewAsync(TKey id, ReviewParameter parameter)
    {
        var result = true;
        await HttpClient.PutAsync(ReviewUrl.Replace("{id}", id.ToString()), parameter, async msg =>
        {
            result = false;
            await Task.CompletedTask;
        });
        return result;
    }
}
public abstract class CrudService<TPagingListViewModel, TPagedParameter, TDetailsViewModel, TCreateViewModel, TUpdateViewModel>
    : CrudService<TPagingListViewModel, Guid, TPagedParameter, TDetailsViewModel, TCreateViewModel, TUpdateViewModel>
    where TDetailsViewModel : class, new()
{
    protected CrudService(HttpApiClient httpClient, IBlazorDownloadFileService downloadFileService) : base(httpClient, downloadFileService)
    {
    }
}
public abstract class CrudService<TPagingListViewModel, TDetailsViewModel, TCreateViewModel, TUpdateViewModel>
    : CrudService<TPagingListViewModel, PagedSortedAndFilterParameter, TDetailsViewModel, TCreateViewModel, TUpdateViewModel>
    where TDetailsViewModel : class, new()
{
    protected CrudService(HttpApiClient httpClient, IBlazorDownloadFileService downloadFileService) : base(httpClient, downloadFileService)
    {
    }
}
public abstract class CrudService<TPagingListViewModel, TDetailsViewModel, TEditViewModel>
    : CrudService<TPagingListViewModel, TDetailsViewModel, TEditViewModel, TEditViewModel>
    where TDetailsViewModel : class, new()
{
    protected CrudService(HttpApiClient httpClient, IBlazorDownloadFileService downloadFileService) : base(httpClient, downloadFileService)
    {
    }
}
public abstract class CrudService<TPagingListViewModel, TEntityViewModel>
    : CrudService<TPagingListViewModel, TEntityViewModel, TEntityViewModel>
    where TEntityViewModel : class, new()
{
    protected CrudService(HttpApiClient httpClient, IBlazorDownloadFileService downloadFileService) : base(httpClient, downloadFileService)
    {
    }
}

public interface ICrudService<TKey> : INameValueService<TKey>, INameNoService
    where TKey : struct
{

}

public interface ICrudDetailsService<TModel, TKey>
    where TModel : class, new()
{
    Task<TModel> GetDetailsAsync(TKey id);
}

public interface ICrudCreateService<TModel>
{
    Task<bool> CreateAsync(TModel model);
}

public interface ICrudUpdateService<TModel, TKey>
{
    Task<bool> UpdateAsync(TKey id, TModel model);
}

public interface ICrudReviewService<TModel, TKey>
{
    Task<bool> ReviewAsync(TKey id, ReviewParameter parameter);
    Task<bool> SubmitReviewAsync(TKey id);
}

public class HttpError
{
    public string Code { get; set; }
    public string Message { get; set; }
    public string Details { get; set; }
}

public class HttpApiClient
{
    private HttpClient _httpClient;
    public HttpClient HttpClient
    {
        get
        {
            if (!Token.IsNullOrWhiteSpace()
                && _httpClient.DefaultRequestHeaders.Authorization?.Parameter != Token)
            {
                _httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", Token);
            }
            if (!TenantId.IsNullOrWhiteSpace()
                && _httpClient.DefaultRequestHeaders.GetValues("__tenant")?.Contains(TenantId) != true)
            {
                _httpClient.DefaultRequestHeaders.Add("__tenant", TenantId);
            }
            return _httpClient;
        }
    }
    internal MessageService MessageService { get; }
    private NavigationManager NavigationManager { get; }

    private static JsonSerializerOptions _JsonOptions;

    static HttpApiClient()
    {
        _JsonOptions = new JsonSerializerOptions
        {
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        };
    }

    public HttpApiClient(HttpClient httpClient, MessageService messageService, NavigationManager navigationManager)
    {
        _httpClient = httpClient;
        MessageService = messageService;
        NavigationManager = navigationManager;
        if (!Token.IsNullOrWhiteSpace())
        {
            _httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", Token);
        }
        if (!TenantId.IsNullOrWhiteSpace())
        {
            _httpClient.DefaultRequestHeaders.Add("__tenant", TenantId);
        }
    }
    public static string Token { get; set; }
    public static string TenantId { get; set; }

    private async Task HandleHttpErrorAsync(HttpResponseMessage message, Func<HttpResponseMessage, Task> handleErrorCallback)
    {
        if (message.StatusCode == HttpStatusCode.Unauthorized)
        {
            Token = null;
            await MessageService.Error("登录超时！", 2, () =>
            {
                NavigationManager.NavigateTo("/login.html");
            });
            if (handleErrorCallback != null)
            {
                await handleErrorCallback(message);
            }
        }
        else
        {
            if ((int)message.StatusCode >= 500)
            {
                await MessageService.Error("服务器异常！", 2);
            }
            else if ((int)message.StatusCode >= 400)
            {
                try
                {
                    var error = await message.Content.ReadFromJsonAsync<Dictionary<string, HttpError>>(_JsonOptions);
                    if (error != null && error.ContainsKey(nameof(error)))
                    {
                        var err = error[nameof(error)];
                        var code = err.Code.IsNullOrWhiteSpace() ? "" : $"({err.Code})";
                        await MessageService.Error(err.Message + code, 2);
                        if (handleErrorCallback != null)
                        {
                            await handleErrorCallback(message);
                        }
                        return;
                    }
                }
                catch
                {
                }
                await MessageService.Error("请求异常！", 2);
            }
            if (handleErrorCallback != null)
            {
                await handleErrorCallback(message);
            }
        }
    }

    private async Task<T> GetValueAsync<T>(HttpResponseMessage message)
        where T : class
    {
        var json = await message.Content.ReadAsStringAsync();
        if (typeof(T) == typeof(string))
        {
            return (T)(object)json;
        }
        var data = JsonSerializer.Deserialize<T>(json, _JsonOptions);
        return data;
    }

    public async Task<string> GetStringAsync<T>(string requestPath, Func<HttpResponseMessage, Task> handleErrorCallback = null)
    {
        return await GetAsync<string>(requestPath, handleErrorCallback);
    }

    public async Task<T> GetAsync<T>(string requestPath, Func<HttpResponseMessage, Task> handleErrorCallback = null)
        where T : class
    {
        var message = await HttpClient.GetAsync(requestPath);
        if (!message.IsSuccessStatusCode)
        {
            await HandleHttpErrorAsync(message, handleErrorCallback);
            return null;
        }
        return await GetValueAsync<T>(message);
    }

    public async Task<T> PostAsync<T>(string requestPath, object parameter, Func<HttpResponseMessage, Task> handleErrorCallback = null)
        where T : class
    {
        var message = await HttpClient.PostAsJsonAsync(requestPath, parameter, _JsonOptions);
        if (!message.IsSuccessStatusCode)
        {
            await HandleHttpErrorAsync(message, handleErrorCallback);
            return null;
        }
        return await GetValueAsync<T>(message);
    }

    public async Task<T> PostFormAsync<T>(string requestPath, Dictionary<string, string> parameter, Func<HttpResponseMessage, Task> handleErrorCallback = null)
        where T : class
    {
        var content = new StringContent(parameter.Select(r => r.Key + "=" + HttpUtility.UrlEncode(r.Value)).JoinAsString("&"));
        var message = await HttpClient.PostAsync(requestPath, content);
        if (!message.IsSuccessStatusCode)
        {
            await HandleHttpErrorAsync(message, handleErrorCallback);
            return null;
        }
        return await GetValueAsync<T>(message);
    }

    public async Task PostAsync(string requestPath, object parameter, Func<HttpResponseMessage, Task> handleErrorCallback = null)
    {
        var message = await HttpClient.PostAsJsonAsync(requestPath, parameter, _JsonOptions);
        if (!message.IsSuccessStatusCode)
        {
            await HandleHttpErrorAsync(message, handleErrorCallback);
        }
    }

    public async Task<T> PutAsync<T>(string requestPath, object parameter, Func<HttpResponseMessage, Task> handleErrorCallback = null)
        where T : class
    {
        var message = await HttpClient.PutAsJsonAsync(requestPath, parameter, _JsonOptions);
        if (!message.IsSuccessStatusCode)
        {
            await HandleHttpErrorAsync(message, handleErrorCallback);
            return null;
        }
        return await GetValueAsync<T>(message);
    }

    public async Task PutAsync(string requestPath, object parameter, Func<HttpResponseMessage, Task> handleErrorCallback = null)
    {
        var message = await HttpClient.PutAsJsonAsync(requestPath, parameter, _JsonOptions);
        if (!message.IsSuccessStatusCode)
        {
            await HandleHttpErrorAsync(message, handleErrorCallback);
        }
    }

    public async Task DeleteAsync(string requestPath, Func<HttpResponseMessage, Task> handleErrorCallback = null)
    {
        var message = await HttpClient.DeleteAsync(requestPath);
        if (!message.IsSuccessStatusCode)
        {
            await HandleHttpErrorAsync(message, handleErrorCallback);
            return;
        }
    }
}
